package com.qinglei.recoup.hmp.service;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.qinglei.recoup.common.utils.JsonUtils;
import com.qinglei.recoup.hmp.domain.HmpUserBindDevice;
import com.qinglei.recoup.hmp.domain.HmpUserTag;
import com.qinglei.recoup.hmp.domain.HmpWechatMpFan;
import com.qinglei.recoup.hmp.pojo.dto.TMAlarmInfoParam;
import com.qinglei.recoup.hmp.pojo.dto.TMDataItemDTO;
import com.qinglei.recoup.hmp.pojo.dto.TMHealthReportParam;
import com.qinglei.recoup.hmp.pojo.dto.TMMiniprogramDTO;
import com.qinglei.recoup.hmp.pojo.dto.TMToUserDTO;
import com.qinglei.recoup.hmp.pojo.dto.TemplateMessageDTO;
import com.qinglei.recoup.hmp.pojo.event.SendMpAlarmInfoEvent;
import com.qinglei.recoup.hmp.pojo.event.SendMpHealthReportEvent;
import com.qinglei.recoup.hmp.pojo.event.WechatMpSubscribeEvent;
import com.qinglei.recoup.system.dao.BedHealthDailyStatisticsMapper;
import com.qinglei.recoup.system.domain.AccessTokenResult;
import com.qinglei.recoup.system.domain.Bed;
import com.qinglei.recoup.system.domain.BedHealthBreathDailyData;
import com.qinglei.recoup.system.domain.BedHealthDailyStatistics;
import com.qinglei.recoup.system.domain.BedHealthQO;
import com.qinglei.recoup.system.domain.BedHealthState;
import com.qinglei.recoup.system.domain.BedRadarBO;
import com.qinglei.recoup.system.domain.Event;
import com.qinglei.recoup.system.domain.User;
import com.qinglei.recoup.system.enums.EventEnum;
import com.qinglei.recoup.system.pojo.HealthVO;
import com.qinglei.recoup.system.service.BedHealthBreathDailyDataService;
import com.qinglei.recoup.system.service.BedHealthDailyStatisticsService;
import com.qinglei.recoup.system.service.BedService;
import com.qinglei.recoup.system.service.EventService;
import com.qinglei.recoup.system.service.UserService;
import com.qinglei.recoup.system.util.WeixinUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

@Service
@Slf4j
public class HmpWechatMpService {
    @Value("${hmp.wechat.mp.appId}")
    private String APPID;

    @Value("${hmp.wechat.mp.appSecret}")
    private String SECRET;

    @Value("${hmp.wechat.mp.url}")
    private String URL;

    @Value("${hmp.wechat.mp.menu}")
    private String qlHealthMpMenu;

    @Value("${wxxcx.appId}")
    private String MINI_PROGRAM_APPID;

    private volatile String access_token;
    private volatile Long access_token_updateTime;

    @Autowired
    private HmpWechatMpFanService hmpWechatMpFanService;

    @Autowired
    private HmpUserBindDeviceService hmpUserBindDeviceService;

    @Autowired
    private HmpUserTagService hmpUserTagService;

    @Autowired
    private EventService eventService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private BedService bedService;

    @Autowired
    private BedHealthDailyStatisticsService dailyStatisticsService;

    @Autowired
    private BedHealthBreathDailyDataService bedHealthBreathDailyDataService;

    @Autowired
    private BedHealthDailyStatisticsMapper bedHealthDailyStatisticsMapper;

    @Autowired
    private UserService userService;

    /**
     * 发送健康报告用线程池
     */
    private Executor sendHealthReportExecutors = Executors.newFixedThreadPool(2);


    /**
     * 获取acctoken
     *
     * @return
     */
    public String getAccessToken() {
        if (access_token != null && (access_token_updateTime + 5400000L) > new Date().getTime()) {
            return access_token;
        }
        synchronized (this) {
            if (access_token != null && (access_token_updateTime + 5400000L) > new Date().getTime()) {
                return access_token;
            }
            log.info("getAccessToken APPID:{},SECRET:{}", APPID, SECRET);
            AccessTokenResult accessTokenResult = restTemplate.getForObject(
                    String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
                            APPID, SECRET),
                    AccessTokenResult.class);
            if (accessTokenResult.getErrcode() == null || accessTokenResult.getErrcode().equals("0")) {
                access_token_updateTime = System.currentTimeMillis();
                access_token = accessTokenResult.getAccess_token();
                log.info("getAccessToken token token:{},expire:{}", accessTokenResult.getAccess_token(), accessTokenResult.getExpires_in());
            } else {
                log.error("getAccessToken errorCode:{},errorMsg:{}", accessTokenResult.getErrcode(), accessTokenResult.getErrmsg());
            }
        }
        return access_token;

    }

    /**
     * 通过公众号OpenID获取用户信息
     *
     * @param openId
     * @return
     */
    public Map<String, Object> getUserInfo(String openId) {
        log.info("getUserUnionId openId:{}", openId);
        String access_token = getAccessToken();
        String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + access_token + "&openid=" + openId;
        JSONObject weixinResult = WeixinUtil.httpRequest(url, "GET", null);
        if (weixinResult == null) {
            log.error("getUserInfo call weixin error,url:{}", url);
            return null;
        }
        if (weixinResult.get("errcode") != null) {
            log.error("getUserInfo call weixin error,errorCode:{},errorMsg:{}", weixinResult.get("errcode"), weixinResult.get("errmsg"));
            return null;
        }
        log.info("getUserInfo call weixin return:{}", weixinResult);
        Map<String, Object> retMap = JsonUtils.jsonToMap(weixinResult.toString(), String.class, Object.class);
        return retMap;
    }

    /**
     * 通过公众号OpenID获取用户信息
     *
     * @param openId
     * @return
     */
    public JSONObject getUserList() {
        log.info("getUserList in");
        String access_token = getAccessToken();
        String url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=" + access_token;
        JSONObject weixinResult = WeixinUtil.httpRequest(url, "GET", null);
        if (weixinResult == null) {
            log.error("getUserList call weixin error,url:{}", url);
            return null;
        }
        if (weixinResult.get("errcode") != null) {
            log.error("getUserList call weixin error,errorCode:{},errorMsg:{}", weixinResult.get("errcode"), weixinResult.get("errmsg"));
            return null;
        }
        log.info("getUserList call weixin return:{}", weixinResult);
        return weixinResult;
    }

    @EventListener
    @Transactional(rollbackFor = {Exception.class})
    public void subscribeEventHandler(WechatMpSubscribeEvent event) {
        log.info("subscribeEventHandler receive event:{}", event);
        if (event.getEvent().equals("subscribe")) {
            this.procSubscribe(event.getFromUserName());
        } else if (event.getEvent().equals("unsubscribe")) {
            this.procUnsubscribe(event.getFromUserName());
        } else {
            log.warn("subscribeEventHandler not support event:{}", event.getEvent());
        }
    }

    /**
     * 发送报警信息事件处理方法
     *
     * @param event
     */
    @EventListener
    @Async
    public void sendAlarmInfoHandler(SendMpAlarmInfoEvent event) {
        log.info("sendAlarmInfoHandler receive event:{}", event);
        Event bedEvent = eventService.getById(event.getEventId());
        if (bedEvent == null) {
            log.warn("sendAlarmInfoHandler bedEvent not exist,event:{}", event);
            return;
        }
        Integer eventType = bedEvent.getType();
        String eventTypeName = null;
        switch (eventType) {
            case BedHealthState.SUB_STATUS_BREATH_HIGHT:
                eventTypeName = EventEnum.ALARM_BREATH_BPM_HIGH.getContent();
                break;
            case BedHealthState.SUB_STATUS_BREATH_LOW:
                eventTypeName = EventEnum.ALARM_BREATH_BPM_LOW.getContent();
                break;
            case BedHealthState.SUB_STATUS_HEART_HIGHT:
                eventTypeName = EventEnum.ALARM_HEART_BPM_HIGH.getContent();
                break;
            case BedHealthState.SUB_STATUS_HEART_LOW:
                eventTypeName = EventEnum.ALARM_HEART_BPM_LOW.getContent();
                break;
            default:
                log.warn("sendAlarmInfoHandler not support event:{}", event);
                return;
        }
        BedRadarBO bed = bedService.getBedWithSn(bedEvent.getBedId());
        if (bed == null) {
            log.warn("sendAlarmInfoHandler bed or radar is not exist, event:{}", event);
            return;
        }
        List<HmpUserBindDevice> bindList = hmpUserBindDeviceService.list(Wrappers.<HmpUserBindDevice>lambdaQuery()
                .eq(HmpUserBindDevice::getDeviceSn, bed.getSnCode()));
        if (CollectionUtils.isEmpty(bindList)) {
            log.warn("sendAlarmInfoHandler not bind mini program event:{}", event);
            return;
        }
        String radarTypeName = "卧室";
        if (bed.getType() == 2) {
            radarTypeName = "卫生间";
        }
        final String alarmName = eventTypeName;
        final String areaInfo = radarTypeName;
        bindList.parallelStream().forEach(hmpBindInfo -> {
            try {
                if (hmpBindInfo.getPoliceStatus() == 0) {
                    log.info("sendAlarmInfoHandler PoliceStatus is off,hmpUserBindDevice:{}", hmpBindInfo);
                    return;
                }
                User user = userService.getById(hmpBindInfo.getUserId());
                if (user == null) {
                    log.info("sendAlarmInfoHandler user is not exist,hmpUserBindDevice:{}", hmpBindInfo);
                    return;
                }
                if (StringUtils.isEmpty(user.getHealthMpOpenId())) {
                    log.info("sendAlarmInfoHandler healthMpOpenId is not set,user:{}", user);
                    return;
                }
                String tagName = null;
                if (hmpBindInfo.getUserTagId() != null) {
                    HmpUserTag userTag = hmpUserTagService.getById(hmpBindInfo.getUserTagId());
                    if (userTag != null) {
                        tagName = userTag.getName();
                    }
                }
                TMAlarmInfoParam templateParam = new TMAlarmInfoParam();
                templateParam.setOpenId(user.getHealthMpOpenId());
                templateParam.setSn(hmpBindInfo.getDeviceSn());
                templateParam.setAlarmName(alarmName);
                templateParam.setAlarmTime(bedEvent.getHappenTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                templateParam.setTagName(tagName);
                templateParam.setAreaInfo(areaInfo);
                this.sendMessage4Alarm(templateParam);
            } catch (Exception e) {
                log.error("sendAlarmInfoHandler error", e);
            }
        });
    }

    /**
     * 发送健康报告事件处理方法
     *
     * @param event
     */
    @EventListener
    @Async
    public void sendHealthReportHandler(SendMpHealthReportEvent event) {
        log.info("sendHealthReportHandler receive event:{}", event);
        List<Integer> bedIds = hmpUserBindDeviceService.selectBedIds4HealthReport();
        log.info("sendHealthReportHandler bedIds:{}", bedIds);
        if (CollectionUtils.isEmpty(bedIds)) {
            return;
        }

        for (Integer bedId : bedIds) {
            CompletableFuture.runAsync(() -> {
                try {
                    sendOneBedHealthReport(bedId, event.getToday());
                } catch (Exception e) {
                    log.error("sendHealthReportHandler error", e);
                }
            }, sendHealthReportExecutors);
        }
    }

    /**
     * 发送一个床位的健康报告
     *
     * @param bedId
     * @param today
     */
    public void sendOneBedHealthReport(Integer bedId, LocalDate today) {
        log.info("sendOneBedHealthReport bedId:{},today:{}", bedId, today);
        List<HmpUserBindDevice> bindUsers = hmpUserBindDeviceService.list(Wrappers.<HmpUserBindDevice>lambdaQuery()
                .eq(HmpUserBindDevice::getBedId, bedId).eq(HmpUserBindDevice::getReportStatus, 1));
        if (CollectionUtils.isEmpty(bindUsers)) {
            log.info("sendOneBedHealthReport bindUsers is empty, bedId:{}", bedId);
            return;
        }
        String sn = bindUsers.get(0).getDeviceSn();

        List<Long> userIds = bindUsers.stream().map(HmpUserBindDevice::getUserId).collect(Collectors.toList());
        List<Integer> userTagIds = bindUsers.stream().map(HmpUserBindDevice::getUserTagId).filter(Objects::nonNull).collect(Collectors.toList());

        Collection<User> users = userService.listByIds(userIds);
        if (CollectionUtils.isEmpty(users)) {
            log.info("sendOneBedHealthReport users is empty, userIds:{}", userIds);
            return;
        }
        Map<Long, User> userMap = users.stream().collect(Collectors.toMap(User::getId, item -> item));

        Map<Integer, HmpUserTag> userTagMap = null;
        Collection<HmpUserTag> hmpUserTags = hmpUserTagService.listByIds(userTagIds);
        if (CollectionUtils.isNotEmpty(hmpUserTags)) {
            userTagMap = hmpUserTags.stream().collect(Collectors.toMap(HmpUserTag::getId, item -> item));
        }
        List<TMToUserDTO> toUserList = new ArrayList<>();

        for (HmpUserBindDevice bindUser : bindUsers) {
            User user = userMap.get(bindUser.getUserId());
            if (StringUtils.isEmpty(user.getHealthMpOpenId())) {
                continue;
            }
            TMToUserDTO tmToUserDTO = new TMToUserDTO();
            tmToUserDTO.setOpenId(user.getHealthMpOpenId());
            if (bindUser.getUserTagId() != null && userTagMap != null && userTagMap.get(bindUser.getUserTagId()) != null) {
                tmToUserDTO.setUserTagName(userTagMap.get(bindUser.getUserTagId()).getName());
            }
            toUserList.add(tmToUserDTO);
        }
        if (CollectionUtils.isEmpty(toUserList)) {
            log.info("sendOneBedHealthReport toUserList is empty, users:{}", users);
            return;
        }
        // 发送健康日报
        this.sendDailyHealthReport(bedId, sn, today, toUserList);
        // 发送健康周报
        this.sendWeeklyHealthReport(bedId, sn, today, toUserList);
        // 发送健康月报
        this.sendMonthlyHealthReport(bedId, sn, today, toUserList);
    }

    /**
     * 发送健康日报
     *
     * @param bedId
     * @param sn
     * @param today
     * @param toUserList
     */
    private void sendDailyHealthReport(Integer bedId, String sn, LocalDate today, List<TMToUserDTO> toUserList) {
        // 获取昨天日期
        LocalDate reportDate = today.minusDays(1);

        BedHealthDailyStatistics dailyStatistics = dailyStatisticsService.getOne(Wrappers.<BedHealthDailyStatistics>lambdaQuery()
                .eq(BedHealthDailyStatistics::getBedId, bedId)
                .eq(BedHealthDailyStatistics::getStatisticsDate, reportDate));
        BedHealthBreathDailyData bedHealthBreathDailyData = bedHealthBreathDailyDataService.getOne(Wrappers.<BedHealthBreathDailyData>lambdaQuery()
                .eq(BedHealthBreathDailyData::getBedId, bedId)
                .eq(BedHealthBreathDailyData::getStatisticsDate, reportDate));
        if (dailyStatistics == null || bedHealthBreathDailyData == null) {
            log.info("sendDailyHealthReport report is empty, bedId:{},date:{}", bedId, reportDate);
            return;
        }

        Bed bed = bedService.getById(bedId);
        if (bed == null) {
            log.info("sendDailyHealthReport bed is not exist, bedId:{}", bedId);
            return;
        }
        Double diagnosisResults = bedHealthBreathDailyData.getAhi();
        Integer flagBreathReport = bedHealthBreathDailyData.getFlagBreathReport();
        String diagnosisResultsStr = null;
        if (bed.getRadarVersion() != null && "3_0".equals(bed.getRadarVersion())) {
            if (0 <= diagnosisResults && 5 > diagnosisResults) {
                diagnosisResultsStr = "健康";
            } else if (0 == flagBreathReport && 5 <= diagnosisResults) {
                diagnosisResultsStr = "需关注";
            } else {
                diagnosisResultsStr = "--";
            }
        } else {
            if (0 == flagBreathReport && 0 <= diagnosisResults && 5 > diagnosisResults) {
                diagnosisResultsStr = "健康";
            } else if (0 == flagBreathReport && 5 <= diagnosisResults) {
                diagnosisResultsStr = "需关注";
            } else {
                diagnosisResultsStr = "--";
            }
        }

        String reportContent = "睡眠得分：" + dailyStatistics.getScore() + "\n" + "睡眠呼吸：" + diagnosisResultsStr;

        TMHealthReportParam param = new TMHealthReportParam();
        param.setDate(reportDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        param.setSn(sn);
        param.setType(1);
        param.setReportTime(param.getDate());
        param.setReportContent(reportContent);

        for (TMToUserDTO tmToUserDTO : toUserList) {
            String title = "您好，您关注的健康报告已经生成";
            if (StringUtils.isNotEmpty(tmToUserDTO.getUserTagName())) {
                title = String.format("您好，您关注的%s的健康报告已经生成", tmToUserDTO.getUserTagName());
            }
            param.setTitle(title);
            param.setOpenId(tmToUserDTO.getOpenId());
            this.sendMessage4HealthReport(param);
        }
    }

    /**
     * 发送健康周报
     *
     * @param bedId
     * @param sn
     * @param today
     * @param toUserList
     */
    private void sendWeeklyHealthReport(Integer bedId, String sn, LocalDate today, List<TMToUserDTO> toUserList) {
        // 判断是否周一
        if (!today.getDayOfWeek().equals(DayOfWeek.MONDAY)) {
            log.info("sendWeeklyHealthReport not monday,today:{}", today);
            return;
        }
        LocalDate startDate = today.minusDays(7);
        LocalDate endDate = today.minusDays(1);

        BedHealthQO bedHealthQO = new BedHealthQO();
        bedHealthQO.setBedId(bedId);
        bedHealthQO.setStartDate(startDate);
        bedHealthQO.setEndDate(endDate);
        HealthVO healthVO = getHealthVO(bedHealthQO);
        if (healthVO == null) {
            log.info("sendWeeklyHealthReport health data is none,bedHealthQO:{}", bedHealthQO);
            return;
        }
        double avgSleepTimes = healthVO.getSleepDuration();
        int sleepHours = (int) (avgSleepTimes / 60);
        int sleepMinutes = (int) (avgSleepTimes - sleepHours * 60);


        String reportContent = "睡眠平均时长：" + sleepHours + "小时" + sleepMinutes + "分钟";

        TMHealthReportParam param = new TMHealthReportParam();
        param.setDate(endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        param.setSn(sn);
        param.setType(3);
        param.setReportTime(startDate.format(DateTimeFormatter.ISO_LOCAL_DATE) + "~" + endDate.format(DateTimeFormatter.ISO_LOCAL_DATE));
        param.setReportContent(reportContent);

        for (TMToUserDTO tmToUserDTO : toUserList) {
            String title = "您好，您关注的上周的健康报告已经生成";
            if (StringUtils.isNotEmpty(tmToUserDTO.getUserTagName())) {
                title = String.format("您好，您关注的%s上周的健康报告已经生成", tmToUserDTO.getUserTagName());
            }
            param.setTitle(title);
            param.setOpenId(tmToUserDTO.getOpenId());
            this.sendMessage4HealthReport(param);
        }

    }

    /**
     * 发送健康月报
     *
     * @param bedId
     * @param sn
     * @param today
     * @param toUserList
     */
    private void sendMonthlyHealthReport(Integer bedId, String sn, LocalDate today, List<TMToUserDTO> toUserList) {
        // 判断是否月第一天
        if (today.getDayOfMonth() != 1) {
            log.info("sendMonthlyHealthReport not 1st day of month,today:{}", today);
            return;
        }
        LocalDate startDate = today.minusMonths(1);
        LocalDate endDate = today.minusDays(1);

        BedHealthQO bedHealthQO = new BedHealthQO();
        bedHealthQO.setBedId(bedId);
        bedHealthQO.setStartDate(startDate);
        bedHealthQO.setEndDate(endDate);
        HealthVO healthVO = getHealthVO(bedHealthQO);
        if (healthVO == null) {
            log.info("sendMonthlyHealthReport health data is none,bedHealthQO:{}", bedHealthQO);
            return;
        }
        double avgSleepTimes = healthVO.getSleepDuration();
        int sleepHours = (int) (avgSleepTimes / 60);
        int sleepMinutes = (int) (avgSleepTimes - sleepHours * 60);


        String reportContent = "睡眠平均时长：" + sleepHours + "小时" + sleepMinutes + "分钟";

        TMHealthReportParam param = new TMHealthReportParam();
        param.setDate(endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        param.setSn(sn);
        param.setType(4);
        param.setReportTime(startDate.format(DateTimeFormatter.ISO_LOCAL_DATE) + "~" + endDate.format(DateTimeFormatter.ISO_LOCAL_DATE));
        param.setReportContent(reportContent);

        for (TMToUserDTO tmToUserDTO : toUserList) {
            String title = "您好，您关注的上月的健康报告已经生成";
            if (StringUtils.isNotEmpty(tmToUserDTO.getUserTagName())) {
                title = String.format("您好，您关注的%s上月的健康报告已经生成", tmToUserDTO.getUserTagName());
            }
            param.setTitle(title);
            param.setOpenId(tmToUserDTO.getOpenId());
            this.sendMessage4HealthReport(param);
        }
    }

    /**
     * 处理粉丝关注事件
     *
     * @param openId
     * @return
     */
    private boolean procSubscribe(String openId) {
        HmpWechatMpFan hmpWechatMpFan = hmpWechatMpFanService.getOne(Wrappers.<HmpWechatMpFan>lambdaQuery().eq(HmpWechatMpFan::getOpenId, openId));
        if (hmpWechatMpFan != null) {
            log.info("procSubscribe openId is exist, openId:{}", openId);
            return true;
        }
        Map<String, Object> userInfo = this.getUserInfo(openId);
        if (userInfo == null) {
            log.error("procSubscribe getUserInfo error,openId:{}", openId);
            return false;
        }
        if (userInfo.get("unionid") == null) {
            log.error("procSubscribe unionID is null,userInfo:{}", userInfo);
            return false;
        }
        hmpWechatMpFan = new HmpWechatMpFan();
        hmpWechatMpFan.setOpenId(openId);
        hmpWechatMpFan.setUnionId((String) userInfo.get("unionid"));
        if (userInfo.get("subscribe_scene") != null) {
            hmpWechatMpFan.setSubscribeScene((String) userInfo.get("subscribe_scene"));
        }
        if (userInfo.get("subscribe_time") != null) {
            hmpWechatMpFan.setSubscribeTime((Integer) userInfo.get("subscribe_time"));
        }
        boolean saveRes = hmpWechatMpFanService.save(hmpWechatMpFan);
        if (!saveRes) {
            log.error("procSubscribe save error,hmpWechatMpFan:{}", hmpWechatMpFan);
            return false;
        }
        // 更新用户表 公众号openID
        User user = userService.getOne(Wrappers.<User>lambdaQuery().eq(User::getUnionid, hmpWechatMpFan.getUnionId()));
        if (user != null) {
            user.setHealthMpOpenId(openId);
            userService.updateById(user);
        }
        log.info("procSubscribe success,openId:{}", openId);
        return true;
    }

    /**
     * 处理粉丝取消关注事件
     *
     * @param openId
     * @return
     */
    private boolean procUnsubscribe(String openId) {
        HmpWechatMpFan hmpWechatMpFan = hmpWechatMpFanService.getOne(Wrappers.<HmpWechatMpFan>lambdaQuery().eq(HmpWechatMpFan::getOpenId, openId));
        if (hmpWechatMpFan == null) {
            log.info("procUnsubscribe openId is not exist, openId:{}", openId);
            return true;
        }
        hmpWechatMpFanService.removeById(hmpWechatMpFan.getId());

        // 更新用户表 公众号openID
        User user = userService.getOne(Wrappers.<User>lambdaQuery().eq(User::getHealthMpOpenId, openId));
        if (user != null) {
            user.setHealthMpOpenId("");
            userService.updateById(user);
        }

        log.info("procUnsubscribe success,openId:{}", openId);
        return true;
    }


    /**
     * 发送报警信息模板消息
     *
     * @param param
     */
    public void sendMessage4Alarm(TMAlarmInfoParam param) {
        String access_token = getAccessToken();
        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + access_token;

        TemplateMessageDTO templateDTO = new TemplateMessageDTO();
        templateDTO.setTouser(param.getOpenId());
        templateDTO.setTemplate_id("NU7GRftRnMwRLbza2R7Doy3_gj1OOAakth75jXp_Zp0");
        templateDTO.setUrl(URL);

        Map<String, TMDataItemDTO> data = new HashMap<>();

        TMDataItemDTO first = new TMDataItemDTO();
        String firstVal = "您关注的设备刚刚发生了一次报警";
        if (StringUtils.isNotEmpty(param.getTagName())) {
            firstVal = String.format("您关注的%s的设备刚刚发生了一次报警", param.getTagName());
        }
        first.setValue(firstVal);
        data.put("first", first);

        TMDataItemDTO keyword1 = new TMDataItemDTO();
        keyword1.setValue(param.getAlarmName());
        data.put("keyword1", keyword1);

        TMDataItemDTO keyword2 = new TMDataItemDTO();
        keyword2.setValue(param.getAreaInfo());
        data.put("keyword2", keyword2);

        TMDataItemDTO keyword3 = new TMDataItemDTO();
        keyword3.setValue(param.getAlarmTime());
        data.put("keyword3", keyword3);

        TMDataItemDTO remark = new TMDataItemDTO();
        remark.setValue("请您及时关注");
        data.put("remark", remark);

        templateDTO.setData(data);

        TMMiniprogramDTO tmMiniprogramDTO = new TMMiniprogramDTO();
        tmMiniprogramDTO.setAppid(MINI_PROGRAM_APPID);
        tmMiniprogramDTO.setPagepath("/pages/realTime/realTime?sn=" + param.getSn());
        templateDTO.setMiniprogram(tmMiniprogramDTO);

        String bodyStr = JsonUtils.objectToJson(templateDTO);
        JSONObject jsonObject = WeixinUtil.httpRequest(url, "POST", bodyStr);
        log.info("sendMessage4Alarm return:{}", jsonObject);
    }

    /**
     * 发送健康报告模板消息
     *
     * @param param
     */
    public void sendMessage4HealthReport(TMHealthReportParam param) {
        String access_token = getAccessToken();
        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + access_token;

        TemplateMessageDTO templateDTO = new TemplateMessageDTO();
        templateDTO.setTouser(param.getOpenId());
        templateDTO.setTemplate_id("nW1OhL4FZTS7fzoFq0eouLDOTFBu_gSAtWmjzRYw9tc");
        templateDTO.setUrl(URL);

        Map<String, TMDataItemDTO> data = new HashMap<>();

        TMDataItemDTO first = new TMDataItemDTO();
        first.setValue(param.getTitle());
        data.put("first", first);

        TMDataItemDTO keyword1 = new TMDataItemDTO();
        keyword1.setValue(param.getReportTime());
        data.put("keyword1", keyword1);

        TMDataItemDTO keyword2 = new TMDataItemDTO();
        keyword2.setValue(param.getReportContent());
        data.put("keyword2", keyword2);

        TMDataItemDTO remark = new TMDataItemDTO();
        remark.setValue("点击可以查看哦～");
        data.put("remark", remark);

        templateDTO.setData(data);

        TMMiniprogramDTO tmMiniprogramDTO = new TMMiniprogramDTO();
        tmMiniprogramDTO.setAppid(MINI_PROGRAM_APPID);
        tmMiniprogramDTO.setPagepath("/pages/reportwx/reportwx?sn=" + param.getSn() + "&date=" + param.getDate()
                + "&type=" + param.getType());
        templateDTO.setMiniprogram(tmMiniprogramDTO);

        String bodyStr = JsonUtils.objectToJson(templateDTO);
        JSONObject jsonObject = WeixinUtil.httpRequest(url, "POST", bodyStr);
        log.info("sendMessage4HealthReport return:{}", jsonObject);
    }

    public HealthVO getHealthVO(BedHealthQO bedHealthQO) {
        HealthVO health = bedHealthDailyStatisticsMapper.selectBedHealth(bedHealthQO);
        return health;
    }


    /**
     * 发送报警信息模板消息
     */
    public void createMenu() {
        String access_token = getAccessToken();
        String url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" + access_token;

        String bodyStr = qlHealthMpMenu;
        JSONObject jsonObject = WeixinUtil.httpRequest(url, "POST", bodyStr);
        log.info("createMenu return:{}", jsonObject);
    }

}
